import argparse
import json
import socket
import time
from nanoleafapi import Nanoleaf, NanoleafRegistrationError, NanoleafConnectionError

# Define a function to remotely invoke a method based on the given JSON request
def remote_invoke(request_json, host, port):
    try:
        # Create a TCP socket
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.connect((host, port))
            # Send the JSON request
            s.sendall(json.dumps(request_json).encode('utf-8'))
            # Receive the response
            response_data = b""
            while True:
                chunk = s.recv(4096)
                if not chunk:
                    break
                response_data += chunk
                try:
                    return json.loads(response_data.decode('utf-8'))
                except json.JSONDecodeError:
                    continue
    except socket.error as e:
        return {"status": -1, "error": str(e)}

# Function to get Nanoleaf layout and token
def get_nanoleaf_layout(ip):
    try:
        nl = Nanoleaf(ip)
        info = nl.get_info()
        # Try to get the auth token using get_auth_token method
        token = nl.get_auth_token()
        print(f"Found layout for Nanoleaf at {ip}")
        if token:
            print(f"Found auth token for Nanoleaf at {ip}")
        return info['panelLayout']['layout'], info['panelLayout']['globalOrientation']['value'], token
    except (NanoleafRegistrationError, NanoleafConnectionError) as e:
        print(f"Error getting layout from Nanoleaf at {ip}: {e}")
        return None, None, None

def rotate_point(x, y, angle_deg):
    """Rotate a point (x, y) by angle (in degrees) around the origin (0, 0)."""
    import math
    angle_rad = math.radians(-angle_deg)  # Rotate clockwise
    cos_theta = math.cos(angle_rad)
    sin_theta = math.sin(angle_rad)
    new_x = x * cos_theta - y * sin_theta
    new_y = x * sin_theta + y * cos_theta
    return new_x, new_y

# Function to remap panel coordinates based on screen resolution
def remap_panels(layout, orientation, screen_width, screen_height):
    margin_x = screen_width * 0.1
    margin_y = screen_height * 0.1

    panels = layout['positionData']
    rotated_positions = []
    for panel in panels:
        if panel['shapeType'] == 12:
            continue  # Skip the controller
        rotated_x, rotated_y = rotate_point(panel['x'], panel['y'], orientation)
        rotated_positions.append((panel['panelId'], rotated_x, rotated_y))

    # Update bounds
    min_x = min(x for _, x, _ in rotated_positions)
    max_x = max(x for _, x, _ in rotated_positions)
    min_y = min(y for _, _, y in rotated_positions)
    max_y = max(y for _, _, y in rotated_positions)

    # Adjust scaling to fit within screen margins
    scale_x = (screen_width - 2 * margin_x) / (max_x - min_x)
    scale_y = (screen_height - 2 * margin_y) / (max_y - min_y)
    scale = min(scale_x, scale_y)

    panel_positions = []
    for panel_id, rotated_x, rotated_y in rotated_positions:
        new_x = int((rotated_x - min_x) * scale + margin_x)
        new_y = int((rotated_y - min_y) * scale + margin_y)
        panel_positions.append({
            "panelID": panel_id,
            "centroidX": new_x,
            "centroidY": new_y
        })

    return panel_positions

def enable_extcontrol(ip):
    try:
        nl = Nanoleaf(ip)
        result = nl.enable_extcontrol()
        print(f"Enabled extcontrol on Nanoleaf device at {ip}: {result}")
    except (NanoleafRegistrationError, NanoleafConnectionError) as e:
        print(f"Error enabling extcontrol on Nanoleaf at {ip}: {e}")
        
# Parse the command line arguments
def main():
    parser = argparse.ArgumentParser(description="Remote invoke a method using JSON format.")
    parser.add_argument("host", type=str, help="The host of the server to connect to.")
    parser.add_argument("port", type=int, help="The port of the server to connect to.")
    parser.add_argument("method", type=str, help="The method to be invoked.")
    parser.add_argument("-i", "--ips", type=str, nargs='*', help="List of IP addresses of the Nanoleaf devices (required for setMirrorConfig)")
    parser.add_argument("-m", "--mode", type=int, help="The mode to set for the configuration (required for setMirrorConfig)")
    parser.add_argument("params", type=str, nargs='*', help="The parameters for the method to be invoked in key=value format.")
    args = parser.parse_args()

    # Construct the JSON request
    request_json = {"method": args.method}
    if args.params:
        try:
            # Parse each parameter and add it to the request JSON
            for param in args.params:
                key, value = param.split('=', 1)
                request_json[key] = value
        except ValueError:
            print(json.dumps({"status": -1, "error": "Invalid parameter format. Use key=value format."}))
            return
    if args.method == "startMirror":
        if not args.ips:
            print(json.dumps({"status": -1, "error": "Missing required argument: ips"}))
            return

        for ip in args.ips:
            if ip.count('.') == 3:
                enable_extcontrol(ip)

        # Simplified request_json for startMirror
        request_json = {"method": "startMirror"}

    elif args.method == "setMirrorConfig":
        if not args.ips or args.mode is None:
            print(json.dumps({"status": -1, "error": "Missing required arguments: ips and mode are required for setMirrorConfig."}))
            return
        # Get screen resolution by invoking getDisplays
        get_displays_request = {"method": "getDisplays"}
        display_result = remote_invoke(get_displays_request, args.host, args.port)
        if display_result.get("status") != 0:
            print(json.dumps({"status": -1, "error": "Failed to get display information."}))
            return

        display_info = display_result.get("result", {}).get("displays", [])[0]
        screen_width = display_info.get("width")
        screen_height = display_info.get("height")
        if screen_width is None or screen_height is None:
            print(json.dumps({"status": -1, "error": "Invalid display information."}))
            return

        # Get display ID if available
        display_id = display_info.get("id", 69733250)  # Default to 69733250 if not found
        
        # Get Nanoleaf layout and remap panels
        devices = []
        for ip in args.ips:
            if ip.count('.') == 3:
                layout, orientation, token = get_nanoleaf_layout(ip)
                if layout is not None:
                    # Create device object
                    device = {
                        "ip": ip,
                        "controlVersion": 2,  # Example version, replace with actual if needed
                        "scaleFactor": 1.0,  # Adjust scale factor as required
                        "shapeType": 2,  # Example shape type, replace with actual
                        "panels": remap_panels(layout, orientation, screen_width, screen_height)
                    }
                    
                    # Add token if available
                    if token:
                        device["token"] = token
                    
                    devices.append(device)

        # Construct JSON data for setMirrorConfig
        set_mirror_config_json = {
            "displayID": display_id,
            "mode": args.mode,
            "devices": devices
        }
        request_json = {
            "method": "setMirrorConfig",
            "config_json": json.dumps(set_mirror_config_json)
        }
    # Perform the remote invocation and print the response
    result = remote_invoke(request_json, args.host, args.port)
    print(json.dumps(result, indent=2))

if __name__ == "__main__":
    main()
